/**
 * Copyright(c) Live2D Inc. All rights reserved.
 *
 * Use of this source code is governed by the Live2D Open Software license
 * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
 */

import {Live2DCubismFramework as cubismmatrix44} from "./cubismmatrix44";
import CubismMatrix44 = cubismmatrix44.CubismMatrix44;

export namespace Live2DCubismFramework
{
    /**
     * カメラの位置変更に使うと便利な4x4行列
     *
     * カメラの位置変更に使うと便利な4x4行列のクラス。
     */
    export class CubismViewMatrix extends CubismMatrix44
    {

        /**
         * コンストラクタ
         */
        public constructor()
        {
            super();
            this._screenLeft = 0.0;
            this._screenRight = 0.0;
            this._screenTop = 0.0;
            this._screenBottom = 0.0;
            this._maxLeft = 0.0;
            this._maxRight = 0.0;
            this._maxTop = 0.0;
            this._maxBottom = 0.0;
            this._maxScale = 0.0;
            this._minScale = 0.0;
        }

        /**
         * 移動を調整
         *
         * @param x X軸の移動量
         * @param y Y軸の移動量
         */
        public adjustTranslate(x: number, y: number): void
        {
            if(this._tr[0] * this._maxLeft + (this._tr[12] + x) > this._screenLeft)
            {
                x = this._screenLeft - this._tr[0] * this._maxLeft - this._tr[12];
            }

            if(this._tr[0] * this._maxRight + (this._tr[12] + x) < this._screenRight)
            {
                x = this._screenRight - this._tr[0] * this._maxRight - this._tr[12];
            }

            if(this._tr[5] * this._maxTop + (this._tr[13] + y) < this._screenTop)
            {
                y = this._screenTop - this._tr[5] * this._maxTop - this._tr[13];
            }

            if(this._tr[5] * this._maxBottom + (this._tr[13] + y) > this._screenBottom)
            {
                y = this._screenBottom - this._tr[5] * this._maxBottom - this._tr[13];
            }

            let tr1: Float32Array = new Float32Array(
                [
                    1.0,    0.0,    0.0,    0.0,
                    0.0,    1.0,    0.0,    0.0,
                    0.0,    0.0,    1.0,    0.0,
                    x,      y,      0.0,    1.0
                ]
            );

            CubismMatrix44.multiply(tr1, this._tr, this._tr);
        }

        /**
         * 拡大率を調整
         *
         * @param cx 拡大を行うX軸の中心位置
         * @param cy 拡大を行うY軸の中心位置
         * @param scale　拡大率
         */
        public adjustScale(cx: number, cy: number, scale: number): void
        {
            let maxScale: number = this.getMaxScale();
            let minScale: number = this.getMinScale();

            let targetScale = scale * this._tr[0];

            if(targetScale < minScale)
            {
                if(this._tr[0] > 0.0)
                {
                    scale = minScale / this._tr[0];
                }
            }
            else if(targetScale > maxScale)
            {
                if(this._tr[0] > 0.0)
                {
                    scale = maxScale / this._tr[0];
                }
            }

            let tr1: Float32Array = new Float32Array(
                [
                    1.0, 0.0, 0.0, 0.0,
                    0.0, 1.0, 0.0, 0.0,
                    0.0, 0.0, 1.0, 0.0,
                    cx, cy, 0.0, 1.0
                ]
            );

            let tr2: Float32Array = new Float32Array(
                [
                    scale, 0.0, 0.0, 0.0,
                    0.0, scale, 0.0, 0.0,
                    0.0, 0.0, 1.0, 0.0,
                    0.0, 0.0, 0.0, 1.0
                ]
            );

            let tr3: Float32Array = new Float32Array(
                [
                    1.0, 0.0, 0.0, 0.0,
                    0.0, 1.0, 0.0, 0.0,
                    0.0, 0.0, 1.0, 0.0,
                    -cx, -cy, 0.0, 1.0,
                ]
            );

            CubismMatrix44.multiply(tr3, this._tr, this._tr);
            CubismMatrix44.multiply(tr2, this._tr, this._tr);
            CubismMatrix44.multiply(tr1, this._tr, this._tr);
        }

        /**
         * デバイスに対応する論理座養生の範囲の設定
         *
         * @param left      左辺のX軸の位置
         * @param right     右辺のX軸の位置
         * @param bottom    下辺のY軸の位置
         * @param top       上辺のY軸の位置
         */
        public setScreenRect(left: number,  right: number, bottom: number, top: number): void
        {
            this._screenLeft = left;
            this._screenRight = right;
            this._screenBottom = bottom;
            this._screenTop = top;
        }

        /**
         * デバイスに対応する論理座標上の移動可能範囲の設定
         * @param left      左辺のX軸の位置
         * @param right     右辺のX軸の位置
         * @param bottom    下辺のY軸の位置
         * @param top       上辺のY軸の位置
         */
        public setMaxScreenRect(left: number, right: number, bottom: number, top: number): void
        {
            this._maxLeft = left;
            this._maxRight = right;
            this._maxTop = top;
            this._maxBottom = bottom;
        }

        /**
         * 最大拡大率の設定
         * @param maxScale 最大拡大率
         */
        public setMaxScale(maxScale: number): void
        {
            this._maxScale = maxScale;
        }

        /**
         * 最小拡大率の設定
         * @param minScale 最小拡大率
         */
        public setMinScale(minScale: number): void
        {
            this._minScale = minScale;
        }

        /**
         * 最大拡大率の取得
         * @return 最大拡大率
         */
        public getMaxScale(): number
        {
            return this._maxScale;
        }

        /**
         * 最小拡大率の取得
         * @return 最小拡大率
         */
        public getMinScale(): number
        {
            return this._minScale;
        }

        /**
         * 拡大率が最大になっているかを確認する
         *
         * @return true 拡大率は最大
         * @return false 拡大率は最大ではない
         */
        public isMaxScale(): boolean
        {
            return this.getScaleX() >= this._maxScale;
        }

        /**
         * 拡大率が最小になっているかを確認する
         *
         * @return true 拡大率は最小
         * @return false 拡大率は最小ではない
         */
        public isMinScale(): boolean
        {
            return this.getScaleX() <= this._minScale;
        }

        /**
         * デバイスに対応する論理座標の左辺のＸ軸位置を取得する
         * @return デバイスに対応する論理座標の左辺のX軸位置
         */
        public getScreenLeft(): number
        {
            return this._screenLeft;
        }

        /**
         * デバイスに対応する論理座標の右辺のＸ軸位置を取得する
         * @return デバイスに対応する論理座標の右辺のX軸位置
         */
        public getScreenRight(): number
        {
            return this._screenRight;
        }

        /**
         * デバイスに対応する論理座標の下辺のY軸位置を取得する
         * @return デバイスに対応する論理座標の下辺のY軸位置
         */
        public getScreenBottom(): number
        {
            return this._screenBottom;
        }

        /**
         * デバイスに対応する論理座標の上辺のY軸位置を取得する
         * @return デバイスに対応する論理座標の上辺のY軸位置
         */
        public getScreenTop(): number
        {
            return this._screenTop;
        }

        /**
         * 左辺のX軸位置の最大値の取得
         * @return 左辺のX軸位置の最大値
         */
        public getMaxLeft(): number
        {
            return this._maxLeft;
        }

        /**
         * 右辺のX軸位置の最大値の取得
         * @return 右辺のX軸位置の最大値
         */
        public getMaxRight(): number
        {
            return this._maxRight;
        }

        /**
         * 下辺のY軸位置の最大値の取得
         * @return 下辺のY軸位置の最大値
         */
        public getMaxBottom(): number
        {
            return this._maxBottom;
        }

        /**
         * 上辺のY軸位置の最大値の取得
         * @return 上辺のY軸位置の最大値
         */
        public getMaxTop(): number
        {
            return this._maxTop;
        }

        private _screenLeft: number;    // デバイスに対応する論理座標上の範囲（左辺X軸位置）
        private _screenRight: number;   // デバイスに対応する論理座標上の範囲（右辺X軸位置）
        private _screenTop: number;     // デバイスに対応する論理座標上の範囲（上辺Y軸位置）
        private _screenBottom: number;  // デバイスに対応する論理座標上の範囲（下辺Y軸位置）
        private _maxLeft: number;       // 論理座標上の移動可能範囲（左辺X軸位置）
        private _maxRight: number;      // 論理座標上の移動可能範囲（右辺X軸位置）
        private _maxTop: number;        // 論理座標上の移動可能範囲（上辺Y軸位置）
        private _maxBottom: number;      // 論理座標上の移動可能範囲（下辺Y軸位置）
        private _maxScale: number;      // 拡大率の最大値
        private _minScale: number;      // 拡大率の最小値
    }
}
